package com.spzx.product.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.spzx.common.core.exception.ServiceException;
import com.spzx.product.api.domain.ProductSku;
import com.spzx.product.api.domain.vo.SkuLockVo;
import com.spzx.product.domain.SkuStock;
import com.spzx.product.mapper.SkuStockMapper;
import com.spzx.product.service.ProductSkuService;
import com.spzx.product.service.SkuStockService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import jakarta.annotation.Resource;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 * 商品sku库存表 服务实现类
 * </p>
 *
 * @author Haoran Sun
 * @since 2024-07-15
 */
@Service
public class SkuStockServiceImpl extends ServiceImpl<SkuStockMapper, SkuStock> implements SkuStockService {


    @Resource(name = "listRedisTemplate")
    private RedisTemplate<String,List<SkuLockVo>> skuLockListRedisTemplate;
    @Resource(name = "stringRedisTemplate")
    private RedisTemplate<String, String> stringRedisTemplate;
    @Resource
    private SkuStockMapper skuStockMapper;
    @Resource
    private ProductSkuService productSkuService;

    @Override
    @Transactional
    public String checkAndLock(String orderNo, List<SkuLockVo> skuLockVoList) {
        // 防抖
        String key = "product:skuStock:checkAndLock:" + orderNo;
        // 缓存skuLockVos的key
        String dataKey = "product:skuStock:skuLockVos:" + orderNo;
        // 防抖
        Boolean isExist = stringRedisTemplate.opsForValue().setIfAbsent(key, orderNo, 1, TimeUnit.HOURS);
        if (Boolean.FALSE.equals(isExist)) {
            if (Boolean.TRUE.equals(skuLockListRedisTemplate.hasKey(dataKey))) {
                //存在-dataKey-已执行过库存锁定
                return "";
            } else {
                // 不存在-dataKey-重复提交
                return "重复提交";
            }
        }
        // 遍历所有商品，验库存并锁库存，要具备原子性
        skuLockVoList.forEach(skuLockVo -> {
            // 验库存：查询，返回的是满足要求的库存列表
            SkuStock skuStock = skuStockMapper.check(skuLockVo.getSkuId(), skuLockVo.getSkuNum());
            // 如果没有一个商品满足要求，这里就验库存失败
            skuLockVo.setHaveStock(null != skuStock);
        });
        // 只要有一个商品锁定失败，所有锁定成功的商品要解锁库存
        if (skuLockVoList.stream().anyMatch(skuLockVo -> !skuLockVo.getHaveStock())) {
            // 获取所有锁定成功的商品，遍历解锁库存
            StringBuilder result = new StringBuilder();
            //获取没有库存的对象列表
            List<SkuLockVo> noHaveStockSkuLockVoList = skuLockVoList
                    .stream()
                    .filter(item -> !item.getHaveStock())
                    .toList();
            for (SkuLockVo noHaveStockSkuLockVo : noHaveStockSkuLockVoList) {
                //解除去重
                stringRedisTemplate.delete(key);
                result.append("商品: ")
                        .append(noHaveStockSkuLockVo.getSkuId())
                        .append(productSkuService.getOne(new LambdaQueryWrapper<ProductSku>()
                                        .eq(ProductSku::getId, noHaveStockSkuLockVo.getSkuId())
                                        .select(ProductSku::getSkuName))
                                .getSkuName())
                        .append(" 库存不足; ");
            }
            //锁定失败，解除
            stringRedisTemplate.delete(key);
            // 响应锁定状态
            return result.toString();
        } else {
            //锁定库存
            skuLockVoList.forEach(skuLockVo -> {
                int row = skuStockMapper.lock(skuLockVo.getSkuId(), skuLockVo.getSkuNum(),orderNo);
                if (row == 0) {
                    //解除去重
                    stringRedisTemplate.delete(key);
                    throw new ServiceException("库存锁定失败");
                }
            });
        }

        // 如果所有商品都锁定成功的情况下，需要缓存锁定信息到redis。以方便将来解锁库存 或者 减库存
        skuLockListRedisTemplate.opsForValue().set(dataKey, skuLockVoList);
        return "";
    }

    @Override
    public void unLock(String orderNo) {
        // 防抖
        String key = "product:skuStock:unlock:" + orderNo;
        // 缓存skuLockVos的key
        String dataKey = "product:skuStock:skuLockVos:" + orderNo;
        // 防抖
        Boolean isExist = stringRedisTemplate.opsForValue().setIfAbsent(key, orderNo, 1, TimeUnit.HOURS);
        if(Boolean.FALSE.equals(isExist)) return;
        // 获取库存的缓存信息
        List<SkuLockVo> skuLockVos = skuLockListRedisTemplate.opsForValue().get(dataKey);
        if (CollectionUtils.isEmpty(skuLockVos)) return;
        // 解锁库存
        skuLockVos.forEach(skuLockVo -> {
            int row = skuStockMapper.unLock(skuLockVo.getSkuId(), skuLockVo.getSkuNum(), orderNo);
            // 解锁失败
            if(row == 0){
                throw new ServiceException("库存解锁失败");
            }
        });
        // 删除锁定库存SkuLockVo的缓存信息
        skuLockListRedisTemplate.opsForValue().getAndDelete(dataKey);
    }

    @Override
    public void minus(String orderNo) {
        // 防抖
        String key = "product:skuStock:minus:" + orderNo;
        // 缓存skuLockVos的key
        String dataKey = "product:skuStock:skuLockVos:" + orderNo;
        // 防抖
        Boolean isExist = stringRedisTemplate.opsForValue().setIfAbsent(key, orderNo, 1, TimeUnit.HOURS);
        if(Boolean.FALSE.equals(isExist)) return;
        // 获取库存的缓存信息
        List<SkuLockVo> skuLockVos = skuLockListRedisTemplate.opsForValue().get(dataKey);
        if (CollectionUtils.isEmpty(skuLockVos)) return;
        // 减库存
        skuLockVos.forEach(skuLockVo -> {
            int row = skuStockMapper.minus(skuLockVo.getSkuId(), skuLockVo.getSkuNum(), orderNo);
            if(row == 0){
                throw new ServiceException("库存减失败");
            }
        });

        // 删除锁定库存SkuLockVo的缓存信息
        skuLockListRedisTemplate.opsForValue().getAndDelete(dataKey);
    }
}
